// ========== Copyright Header Begin ==========================================
// 
// OpenSPARC T1 Processor File: RegisterFile.cc
// Copyright (c) 2006 Sun Microsystems, Inc.  All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// 
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// 
// The above named program is distributed in the hope that it will be 
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
// 
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// 
// ========== Copyright Header End ============================================
/************************************************************************
**  
**  Copyright (C) 2002, Sun Microsystems, Inc.
**
**  Sun considers its source code as an unpublished, proprietary
**  trade secret and it is available only under strict license provisions.
**  This copyright notice is placed here only to protect Sun in the event 
**  the source is deemed a published work. Disassembly, decompilation,
**  or other means of reducing the object code to human readable form
**  is prohibited by the license agreement under which this code is
**  provided to the user or company in possession of this copy."
**
*************************************************************************/
#include <RegisterFile.h>
#include <StdMacros.h>
#include <strings.h>
#include <string.h>
#include <sstream>
#include <iomanip>
#include <cassert>
#include "CallbackInterface.h"

using namespace std;
using namespace Riesling;

const UintT   RegisterFile::DEFAULT_NWINDOWS;
const UintT   RegisterFile::DEFAULT_GLOBAL_COUNT;
const UintT   RegisterFile::GLOBAL_SET_SIZE;
const UintT   RegisterFile::WINDOW_SIZE;
const uint32_t RegisterFile::WIN_STATE_BITS;
const uint32_t RegisterFile::REG_NUM_BITS;

const string RegisterFile::REGNUM_2_STRING[] = {
		    "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
		    "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7",
		    "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
		    "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7"
		};


#define FLATTEN( cwp, reg ) (((cwp * WINDOW_SIZE) + (31 - reg)) % nrWindowRegs_)

///////////////////////////////////////////////////////////////

RegisterFile::RegisterFile( UintT nwindows, UintT globals ) : 
    cwp_(0),
    cansave_((nwindows - 2) & WIN_STATE_BITS),
    canrestore_(0),
    otherwin_(0),
    globalSet_(0),
    nwindows_(nwindows),
    cleanwin_(nwindows - 2), // This number comes from JPS1 docs.
    globals_(globals),
    registers_( new RegisterT[totalRegs( globals, nwindows)] ),
    depLds_( totalRegs( globals, nwindows ), 0 ),
    eccregs_( new uint64_t[totalRegs( globals, nwindows)] )
{
    if( cansave_ + canrestore_ + otherwin_ != nwindows_ - 2 ){
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"CANSAVE + CANRESTORE + OTHERWIN != NWINDOWS - 2", 
	        "Sparc Architecture Manual, Section 6.4.1" 
		);
    }

    if( nwindows_ < 3 || nwindows_ > 32 ){
	RIESLING_THROW_SPARC_ARCH_VIOLATION( 
		"NWINDOWS < 3",
	        "Sparc Architecture Manual, Section 5.1.1.2"
		);
    }

    nrWindowRegs_ = nwindows_ * WINDOW_SIZE;

    int i, j = globals_ * GLOBAL_SET_SIZE + nwindows_ * WINDOW_SIZE;
    for (i=0; i<j; i++)
    {
      registers_[i] = 0;
      eccregs_[i] = 0;
    }

    updRCgs();
    updRCcwp();
    updECCgs();
    updECCcwp();
} 

///////////////////////////////////////////////////////////////

RegisterFile::~RegisterFile()
{}

///////////////////////////////////////////////////////////////

void RegisterFile::updRCgs()
{
  regCache_[0] = &registers_[globalSet_ * GLOBAL_SET_SIZE + 7];
  regCache_[4] = &registers_[6 * GLOBAL_SET_SIZE + 7];
  regCache_[5] = &registers_[7 * GLOBAL_SET_SIZE + 7];
}

void RegisterFile::updRCcwp()
{
  uint_t i = (globals_ * GLOBAL_SET_SIZE);
  uint_t j = FLATTEN( cwp_, 8 );
  regCache_[1] = &registers_[i+j];
  j = FLATTEN( cwp_, 16 );
  regCache_[2] = &registers_[i+j];
  regCache_[3] = &registers_[i+j-8];
}

///////////////////////////////////////////////////////////////

void RegisterFile::updECCgs()
{
  eccCache_[0] = &eccregs_[globalSet_ * GLOBAL_SET_SIZE + 7];
  eccCache_[4] = &eccregs_[6 * GLOBAL_SET_SIZE + 7];
  eccCache_[5] = &eccregs_[7 * GLOBAL_SET_SIZE + 7];
}

void RegisterFile::updECCcwp()
{
  uint_t i = (globals_ * GLOBAL_SET_SIZE);
  uint_t j = FLATTEN( cwp_, 8 );
  eccCache_[1] = &eccregs_[i+j];
  j = FLATTEN( cwp_, 16 );
  eccCache_[2] = &eccregs_[i+j];
  eccCache_[3] = &eccregs_[i+j-8];
}

///////////////////////////////////////////////////////////////

bool RegisterFile::operator==( const RegisterFile &rhs ) const
{
    int i, j = globals_ * GLOBAL_SET_SIZE + nwindows_ * WINDOW_SIZE;
    for (i=0; i<j; i++)
    {
      if (registers_[i] != rhs.registers_[i])
      {
	return false;
      }
      if (eccregs_[i] != rhs.eccregs_[i])
      {
        return false;
      }
    }
    return cwp_          == rhs.cwp_ &&
	   cansave_      == rhs.cansave_ &&
	   canrestore_   == rhs.canrestore_ &&
	   otherwin_     == rhs.otherwin_ &&
	   cleanwin_     == rhs.cleanwin_ &&
	   nwindows_     == rhs.nwindows_ &&
	   globals_      == rhs.globals_ &&
	   globalSet_    == rhs.globalSet_ &&
	   nrWindowRegs_ == rhs.nrWindowRegs_ &&
           depLds_       == rhs.depLds_;
}

///////////////////////////////////////////////////////////////

const RegisterFile& RegisterFile::operator=( const RegisterFile &rhs )
{
    globals_ = rhs.globals_;
    nrWindowRegs_ = rhs.nrWindowRegs_; 
    cwp_ = rhs.cwp_;
    cansave_ = rhs.cansave_;
    canrestore_ = rhs.canrestore_;
    otherwin_ = rhs.otherwin_;
    cleanwin_ = rhs.cleanwin_;
    nwindows_ = rhs.nwindows_;
    globalSet_ = rhs.globalSet_;
    depLds_  = rhs.depLds_ ;
    int i, j = globals_ * GLOBAL_SET_SIZE + nwindows_ * WINDOW_SIZE;
    for (i=0; i<j; i++)
    {
      registers_[i] = rhs.registers_[i];
      eccregs_[i] = rhs.eccregs_[i];
    }
    updRCgs();
    updRCcwp();
    if (CallbackInterface::getEnableRAS())
    {
      updECCgs();
      updECCcwp();
    }
    return *this;
}


///////////////////////////////////////////////////////////////

bool RegisterFile::windowSpillTrap() const
{
    return cansave_ == 0;
}

///////////////////////////////////////////////////////////////

bool RegisterFile::cleanWindowTrap() const
{
    return cansave_ != 0 && (cleanwin_ - canrestore_) == 0;
}


///////////////////////////////////////////////////////////////

bool RegisterFile::windowFillTrap() const
{
    return canrestore_ == 0;
}

///////////////////////////////////////////////////////////////

bool RegisterFile::save()
{
    if( windowSpillTrap() || cleanWindowTrap() ){
	return false;
    }
    
    moduloIncrement( cwp_ );
    updRCcwp();
    if (CallbackInterface::getEnableRAS())
    {
      updECCcwp();
    }
    moduloDecrement( cansave_ );
    moduloIncrement( canrestore_ );

    return true;
}

///////////////////////////////////////////////////////////////

bool RegisterFile::restore()
{
    if( windowFillTrap() ){
	return false;
    }
    moduloDecrement( cwp_ );
    updRCcwp();
    if (CallbackInterface::getEnableRAS())
    {
      updECCcwp();
    }
    moduloIncrement( cansave_ );
    moduloDecrement( canrestore_ );
    
    return true;
}

///////////////////////////////////////////////////////////////

void RegisterFile::saved()
{
    moduloIncrement( cansave_ );

    if( otherwin_ != 0 ){
	moduloDecrement( otherwin_ );
    } else {
	moduloDecrement( canrestore_ );
    }
}

///////////////////////////////////////////////////////////////

void RegisterFile::restored()
{
    moduloIncrement( canrestore_ );

    if( otherwin_ != 0 ){
	moduloDecrement( otherwin_ );
    } else {
	moduloDecrement( cansave_ );
    }

    if( cleanwin_ < (nwindows_ - 1 ) ){
	moduloIncrement( cleanwin_ );
    } 
}

///////////////////////////////////////////////////////////////

bool RegisterFile::flushw( )
{
    return nwindows_ - 2 != cansave_;
}

///////////////////////////////////////////////////////////////

string RegisterFile::toString() const
{
    ostringstream os;

    for( int i = GLOBAL_SET_SIZE; i < 32; ++i ){
	os << REGNUM_2_STRING[i] << "=0x" << hex << setw(16) << setfill('0') << get( i );
	if( i - GLOBAL_SET_SIZE != 0 && !((i - GLOBAL_SET_SIZE + 1) % 4)){
	    os << "\n";
	} else {
	    os << " ";
	}
    }

    os << "\n";

    for( int i = 0; i < globals_; ++i ){
	os << "set " << i << ": ";
	for( int j = 0; j < GLOBAL_SET_SIZE/2; ++j ){
	    os << REGNUM_2_STRING[j] << "=0x" << hex << setw(16) << 
		setfill('0') << registers_[getIdx(i, j)] << " ";
	}
	os << "\n       ";
	for( int j = GLOBAL_SET_SIZE/2; j < GLOBAL_SET_SIZE; ++j ){
	    os << REGNUM_2_STRING[j] << "=0x" << hex << setw(16) << 
		setfill('0') << registers_[getIdx( i, j)] << " ";
	}
	os << "\n";
    }

    return os.str();
}

///////////////////////////////////////////////////////////////

void RegisterFile::print( ostream& os ) const
{
    os << toString();
}

///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getCWP() const
{
    return cwp_;
}


///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getCANSAVE() const
{
    return cansave_;
}

///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getCANRESTORE() const
{
    return canrestore_;
}

///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getOTHERWIN() const
{
    return otherwin_;
}

///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getNWINDOWS() const
{
    return nwindows_;
}

///////////////////////////////////////////////////////////////

uint32_t RegisterFile::getCLEANWIN() const
{
    return cleanwin_;
}

///////////////////////////////////////////////////////////////

void RegisterFile::setCWP( uint32_t value )
{
    if (value != cwp_) {
      moduloConvert( value );
      cwp_ = value & WIN_STATE_BITS;
      updRCcwp();
      if (CallbackInterface::getEnableRAS())
      {
        updECCcwp();
      }
    }
}

///////////////////////////////////////////////////////////////

void RegisterFile::setCANSAVE( uint32_t value )
{
    moduloConvert( value );
    cansave_ = value & WIN_STATE_BITS;
}

///////////////////////////////////////////////////////////////

void RegisterFile::setCANRESTORE( uint32_t value )
{
    moduloConvert( value );
    canrestore_ = value & WIN_STATE_BITS;
}

///////////////////////////////////////////////////////////////

void RegisterFile::setOTHERWIN( uint32_t value )
{
    moduloConvert( value );
    otherwin_ = value & WIN_STATE_BITS;
}

///////////////////////////////////////////////////////////////

void RegisterFile::setCLEANWIN( uint32_t value )
{
    moduloConvert( value );
    cleanwin_ = value & WIN_STATE_BITS;
}

///////////////////////////////////////////////////////////////

void RegisterFile::moduloConvert( uint32_t &value )
{
    int32_t   count;

    count = (int32_t ) value;
    if( count < 0 ){
	value = nwindows_ - ((-count) % nwindows_);
	if(value == nwindows_)
	    value = 0;
    } else {
        value = count % nwindows_;
    }
}

///////////////////////////////////////////////////////////////
void RegisterFile::moduloDecrement( uint32_t &value )
{
    --value;
    moduloConvert( value );
}

///////////////////////////////////////////////////////////////
void RegisterFile::moduloIncrement( uint32_t &value )
{
    ++value;
    moduloConvert( value );
}

///////////////////////////////////////////////////////////////
void RegisterFile::selectGlobalSet( unsigned set )
{
    if( set >= globals_ ){
	RIESLING_THROW_INVALID_ARGUMENT( "Global set selector out of range" );
    }

    globalSet_ = set;
    updRCgs();
    if (CallbackInterface::getEnableRAS())
    {
      updECCgs();
    }
}

///////////////////////////////////////////////////////////////

uint64_t RegisterFile::get( uint32_t sel, uint32_t reg ) const
{
    reg &= REG_NUM_BITS;

    return registers_[getIdx( sel, reg)];
}

///////////////////////////////////////////////////////////////

void RegisterFile::set( uint32_t sel, uint32_t reg, uint64_t value )
{
    reg &= REG_NUM_BITS;

    if( !reg ){ 
	return;
    }

    registers_[getIdx( sel, reg )] = value;
}

///////////////////////////////////////////////////////////////

uint_t RegisterFile::flatten( uint_t cwp, uint_t reg ) const
{
    return FLATTEN( cwp, reg );
}

///////////////////////////////////////////////////////////////

void RegisterFile::incLdDep( uint_t reg )
{
    ++depLds_[getIdx( reg )];
}

///////////////////////////////////////////////////////////////

void RegisterFile::decLdDep( uint_t reg )
{
    uint_t idx = getIdx( reg );
    if( depLds_[idx] ){
	--depLds_[idx];
    } else {
	RIESLING_THROW_DOMAIN_ERROR( "Attempt to decrement dependent load count for registers with 0 dependent loads." );
    }
}

///////////////////////////////////////////////////////////////

void RegisterFile::decLdDep( uint_t sel, uint_t reg )
{
    uint_t idx = getIdx( sel, reg );
    if( depLds_[idx] ){
	--depLds_[idx];
    } else {
	RIESLING_THROW_DOMAIN_ERROR( "Attempt to decrement dependent load count for registers with 0 dependent loads." );
    }
}

///////////////////////////////////////////////////////////////

uint_t RegisterFile::getIdx( uint_t sel, uint_t reg ) const
{
    uint_t result = 0;

    if( reg < GLOBAL_SET_SIZE ){
	if( sel >= globals_ ){
	    RIESLING_THROW_INVALID_ARGUMENT( "invalid global set" );
	}
	result = sel * GLOBAL_SET_SIZE + (7 - reg);
    }
    else if (reg >= TEMP_INDEX_START)
    {
        // map temp registers to global set 6, 7, ...
        result = getIdx((6 + (reg - TEMP_INDEX_START) >> 3),reg & 7);
    }
    else
    {
	uint32_t cwp = sel & WIN_STATE_BITS;
	if( cwp >= nwindows_ ){
	    RIESLING_THROW_INVALID_ARGUMENT( "cwp out of range" );
	}
	result = (globals_ * GLOBAL_SET_SIZE) + flatten( cwp, reg );
    }

    return result;
}

///////////////////////////////////////////////////////////////

uint_t RegisterFile::getIdx( uint_t reg ) const
{
    uint_t result = 0;

    if ( reg < GLOBAL_SET_SIZE ) {
        result = globalSet_ * GLOBAL_SET_SIZE + (7 - reg);
    }
    else if (reg >= TEMP_INDEX_START)
        // reg 32 ... 47 is %t0 .. %t15 and map to global sets 6 and 7 :-)
        result = getIdx((6 + (reg - TEMP_INDEX_START) >> 3) & 7,reg & 7);
    else
    {
        uint32_t cwp = cwp_ & WIN_STATE_BITS;
        result = (globals_ * GLOBAL_SET_SIZE) + FLATTEN( cwp, reg );
    }

    return result;
}

///////////////////////////////////////////////////////////////

uint_t RegisterFile::totalRegs( uint_t globals, uint_t nwindows )
{
    return globals * GLOBAL_SET_SIZE + nwindows * WINDOW_SIZE;
} 

///////////////////////////////////////////////////////////////

const string& RegisterFile::reg2Str( uint_t reg )
{
    return REGNUM_2_STRING[ reg & 0x03f ];
}

///////////////////////////////////////////////////////////////

void RegisterFile::snapshot( SS_SnapShot &ss, const char *prefix )
{
    // char extended_prefix[SS_SnapShot::LINE_SIZE];

    sprintf(ss.tag,"%s.cwp",prefix); ss.val(&cwp_);
    sprintf(ss.tag,"%s.cansave",prefix); ss.val(&cansave_);
    sprintf(ss.tag,"%s.canrestore",prefix); ss.val(&canrestore_);
    sprintf(ss.tag,"%s.otherwin",prefix); ss.val(&otherwin_);
    sprintf(ss.tag,"%s.cleanwin",prefix); ss.val(&cleanwin_);
    sprintf(ss.tag,"%s.nwindows",prefix); ss.val(&nwindows_);
    sprintf(ss.tag,"%s.globals",prefix); ss.val(&globals_);
    sprintf(ss.tag,"%s.globalSet",prefix); ss.val(&globalSet_);

		// RegisterT *registers_;  // register values globals + windows
    int i, j = globals_ * GLOBAL_SET_SIZE + nwindows_ * WINDOW_SIZE;
    for (i=0; i<j; i++) {
      sprintf(ss.tag,"%s.reg%d",prefix,i);	ss.val(&(registers_[i]));
    }
    if (ss.do_load()) {
	updRCgs();
	updRCcwp();
    }

		// LoadDepVecT depLds_;     // dependent load count ----> May not need to checkpoint this!!!!!

    sprintf(ss.tag,"%s.nrWindowRegs",prefix); ss.val(&nrWindowRegs_);
}

///////////////////////////////////////////////////////////////

void RegisterFile::doRandomInit()
{
    int i, j = globals_ * GLOBAL_SET_SIZE + nwindows_ * WINDOW_SIZE;
    for (i=0; i<j; i++) 
      registers_[i] = (uint64_t(rand()) << 32) | uint64_t(rand());

    for (i=0; i<globals_; i++)
      registers_[i * GLOBAL_SET_SIZE + 7] = 0;
}


